<template>
  <div class="goods-sku">
    <!-- 外循环生成行，有哪些规格：eg:颜色，尺寸 -->
    <dl v-for="spec in goods.specs" :key="spec.name">
      <dt>{{ spec.name }}</dt>
      <dd>
        <!-- 内循环生成列：每种规格有哪些可供选择的具体项，eg:黑色、蓝色、白色等 -->
        <template v-for="btnObj in spec.values" :key="btnObj.name">
          <!-- 有图片优先显示图片 -->
          <!--
            这里的 btnObj.selected 属性是自定义的，后台给的数据是不存在这个属性的
            作用：通过这个属性值的true或false控制当前按钮是否选中
           -->
          <img
            v-if="btnObj.picture"
            :class="{ selected: btnObj.selected, disabled: btnObj.disabled }"
            @click="changeSku(btnObj, spec)"
            :src="btnObj.picture"
          />
          <!-- 否则显示文字 -->
          <span
            v-else
            @click="changeSku(btnObj, spec)"
            :class="{ selected: btnObj.selected, disabled: btnObj.disabled }"
            >{{ btnObj.name }}</span
          >
        </template>
      </dd>
    </dl>
  </div>
</template>
<script>
import powerSet from "@/vendor/power-set";
const spliter = "⭐️";
// 得到路径对象
const getPathMap = (skus) => {
  // 存储路径对象
  const pathMap = {};
  skus.forEach((sku) => {
    if (sku.inventory > 0) {
      // 得到有效sku规则的数组
      const valueArr = sku.specs.map((item) => item.valueName);
      // 得到所有可组合的sku集合
      const valueArrPowerSet = powerSet(valueArr);
      valueArrPowerSet.forEach((arr) => {
        const key = arr.join(spliter);
        if (pathMap[key]) {
          pathMap[key].push(sku.id);
        } else {
          pathMap[key] = [sku.id];
        }
      });
    }
  });
  return pathMap;
};

// 获取选中的值 下面两种写法都可以
// const getSelectedValues = (specs) => {
//   const arr = []
//   specs.forEach((item) => {
//     const val = item.values.find((val) => val.selected)
//     // return val?.name
//     if (val) {
//       arr.push(val.name)
//     } else {
//       arr.push(undefined)
//     }
//   })
//   return arr
// }
// 获取选中的值
const getSelectedValues = (specs) => {
  return specs.map((item) => {
    const val = item.values.find((val) => val.selected);
    return val?.name;
  });
};
// 更新按钮的禁用状态
const updateDisabledStatus = (specs, pathMap) => {
  specs.forEach((item, i) => {
    const selectedValues = getSelectedValues(specs);
    item.values.forEach((val) => {
      // if (pathMap[val.name]) { // 在字典中，不需要禁用
      //   val.disabled = false
      // } else { // 需要禁用
      //   val.disabled = true
      // }
      // 等同于上方代码
      // val.disabled = !pathMap[val.name]
      // 如果当前按钮已选中，则无需禁用
      if (val.selected) return;
      // 存储新选中的规格
      selectedValues[i] = val.name;
      // 根据选中的数组变成字符串的key
      const key = selectedValues.filter((v) => v).join(spliter);
      if (pathMap[key]) {
        // 可选，无需禁用
        val.disabled = false;
      } else {
        // 禁用
        val.disabled = true;
      }
      // val.disabled = !pathMap[val.name]
    });
  });
};

// 初始化默认选中的具体规则（回显）
const initDefaultSelected = (goods, skuId) => {
  // 根据 skuId 去 goods.skus 中查找sku对象
  const sku = goods.skus.find((sku) => sku.id === skuId);
  if (!sku) return;
  // 获取 sku.specs 中每个规格的值，去 goods.specs 中做比对。
  // 如果 sku.specs的值与goods.specs中的值全等，就让当前按钮的selected变为true
  // 遍历 goods.specs 数组
  goods.specs.forEach((item, index) => {
    // 根据 name 查找
    const btnObj = item.values.find(
      (val) => val.name === sku.specs[index].valueName
    );
    // btnObj.selected = btnObj
    // console.log('🚀 ~ file: goods-sku.vue:114 ~ goods.specs.forEach ~ btnObj.selected:', btnObj.selected)
    if (btnObj) {
      // 找到了，让当前按钮对象选中，selected变为true
      btnObj.selected = true;
    } else {
      // 没找到，不选中
      btnObj.selected = false;
    }
  });
};

export default {
  name: "GoodsSku",
  props: {
    goods: {
      type: Object,
      default: () => {
        // 这样写就是保证对象里面必须要有这两个属性
        // skus:选择到的具体的商品的规则数组
        // specs:可供选择的规格
        return { skus: [], specs: [] };
      },
    },
    skuId: {
      type: String,
      // default: '300270666'
      default: "",
    },
  },
  setup(props, { emit }) {
    // 初始化回显选中状态
    initDefaultSelected(props.goods, props.skuId);
    // 调用函数，生成规格（组合）的路径字典（对象）
    const pathMap = getPathMap(props.goods.skus);
    // console.log(pathMap)
    // 点击了按钮之后，更新按钮的禁用状态
    updateDisabledStatus(props.goods.specs, pathMap);
    // console.log('111111', props.goods)
    // 切换按钮是否选中
    const changeSku = (btnObj, spec) => {
      // 如果禁用，提前结束
      if (btnObj.disabled) return;
      // console.log(btnObj)
      // 让同类（平级的）统统不选中 --> 排他思想
      spec.values.forEach((btnObj) => (btnObj.selected = false));
      // 如果这个属性是true,说明原来是选中的
      if (btnObj.selected) {
        btnObj.selected = false;
      } else {
        // 如果原没有这个属性，或者值为false，说明原来是未选中的，点完后变为那么变为true
        btnObj.selected = true;
      }
      // btnObj.selected = !btnObj.selected
      // 点击了按钮之后，更新按钮的禁用状态
      updateDisabledStatus(props.goods.specs, pathMap);

      // 判断所有的规则都选中了
      // 拿到所有选中值的数组
      const values = getSelectedValues(props.goods.specs);
      // 过滤掉undefined
      const selectedValues = values.filter((v) => v);
      // 过滤了之后，数组的长度与 goods.specs 的长度做比对
      if (selectedValues.length === props.goods.specs.length) {
        // 所有的规格全选中了
        // 收集选中的数据（sku对象）
        // 根据选中值的数组，['黑色', '30', '日本]，通过join()拼接成一个字符串的key
        const key = selectedValues.join(spliter);
        // 去字典中查找skuId
        const [skuId] = pathMap[key];
        // 根据 skuId 去 goods.skus 数组中查找sku对象
        const skuObj = props.goods.skus.find((sku) => sku.id === skuId);
        // 对规格数组做个格式化处理，得到字符串文本
        const str = skuObj.specs
          .map((item) => `${item.name}:${item.valueName}`)
          .join(" ");
        // ...语法: 重要！！！
        // 作用：展开对象/数组、浅拷贝对象/数组、合并对象/数组，收集函数剩余参数
        emit("finish", {
          ...skuObj, // 展开对象所有属性
          // 新增一个规格的文本字符串
          specText: str,
        });
      } else {
        // 没有全部选中
        emit("finish", null);
      }
    };
    return {
      changeSku,
    };
  },
};
</script>
<style scoped lang="less">
.sku-state-mixin () {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;
  &.selected {
    border-color: @xtxColor;
  }
  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed;
  }
}
.goods-sku {
  padding-left: 10px;
  padding-top: 20px;
  dl {
    display: flex;
    padding-bottom: 20px;
    align-items: center;
    dt {
      width: 50px;
      color: #999;
    }
    dd {
      flex: 1;
      color: #666;
      > img {
        width: 50px;
        height: 50px;
        .sku-state-mixin ();
      }
      > span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        .sku-state-mixin ();
      }
    }
  }
}
</style>
